package org.atomojo.app.client;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.infoset.xml.Document;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.Cookie;
import org.restlet.data.Method;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.representation.Representation;

/**
 * This class provides a way to script interations with a APP-enabled
 * server.  You can write scripts as an XML document and then load
 * and run them via this class.  In all the examples, the script uses
 * the namespace {http://www.atomojo.org/Vocabulary/APPInteraction/2010/1/0}
 *
 * The basic structure is:
 *
 * {@code
 * <interaction>
 * (get|create|update|delete|for-feed|loop)+
 * </interaction>
 *
 * <loop variable=xs:NCName start=xs:integer end=xs:integer>
 * (for-feed|loop)+
 * </loop>
 *
 * <for-feed href=xs:anyURI>
 * (get|create|update|delete|loop)+
 * </for-feed>
 * }
 * @author alex
 */
public class APPInteraction {

   interface Action {
      void apply(URI baseURI)
         throws Exception;
   }

   class SequenceAction implements Action {
      List<Action> actions = new ArrayList<Action>();
      public void apply(URI baseURI)
         throws Exception
      {
         for (Action action : actions) {
            action.apply(baseURI);
         }
      }
   }

   class CreateAction implements Action {
      String href;
      Representation content;
      CreateAction(String href,Representation content) {
         this.href = href;
         this.content = content;
      }
      public void apply(URI baseURI)
         throws Exception
      {
         post(new Reference(baseURI.resolve(href).toString()),content);
      }
   }

   class UpdateAction implements Action {
      String href;
      Representation content;
      UpdateAction(String href,Representation content) {
         this.href = href;
         this.content = content;
      }
      public void apply(URI baseURI)
         throws Exception
      {
         put(new Reference(baseURI.resolve(href).toString()),content);
      }
   }

   class DeleteAction implements Action {
      String href;
      DeleteAction(String href) {
         this.href = href;
      }
      public void apply(URI baseURI)
         throws Exception
      {
         delete(new Reference(baseURI.resolve(href).toString()));
      }
   }

   class GetAction implements Action {
      String href;
      GetAction(String href) {
         this.href = href;
      }
      public void apply(URI baseURI)
         throws Exception
      {
         get(new Reference(baseURI.resolve(href).toString()));
      }
   }

   class HeadAction implements Action {
      String href;
      HeadAction(String href) {
         this.href = href;
      }
      public void apply(URI baseURI)
         throws Exception
      {
         head(new Reference(baseURI.resolve(href).toString()));
      }
   }

   Context context;
   URI docBaseURI;
   Action root;
   Cookie cookie;
   Identity identity;
   Map<String,Client> clients;

   public APPInteraction(Context context) {
      this.context = context;
      root = new SequenceAction();
      clients = new HashMap<String,Client>();
   }

   public void setCookie(Cookie cookie)
   {
      this.cookie = cookie;
   }

   public void setIdentity(String username,String password)
   {
      this.identity = new Identity(username,password);
   }

   public void setIdentity(Identity identity)
   {
      this.identity = identity;
   }

   protected Client getClient(String scheme) {
      Client client = clients.get(scheme);
      if (client==null) {
         client = new Client(context,Protocol.valueOf(scheme));
         clients.put(scheme,client);
      }
      return client;
   }
   protected Response get(Reference uri)
   {
      Client client = getClient(uri.getScheme());
      Request request = new Request(Method.GET,uri);
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      return client.handle(request);
   }

   protected Response head(Reference uri)
   {
      Client client = getClient(uri.getScheme());
      Request request = new Request(Method.HEAD,uri);
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      return client.handle(request);
   }

   protected Response post(Reference uri,Representation entity)
   {
      Client client = getClient(uri.getScheme());
      Request request = new Request(Method.POST,uri);
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      request.setEntity(entity);
      return client.handle(request);
   }
   
   protected Response put(Reference uri,Representation entity)
   {
      Client client = getClient(uri.getScheme());
      Request request = new Request(Method.PUT,uri);
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      request.setEntity(entity);
      return client.handle(request);
   }

   protected Response delete(Reference uri)
   {
      Client client = getClient(uri.getScheme());
      Request request = new Request(Method.DELETE,uri);
      if (identity!=null) {
         request.setChallengeResponse(new ChallengeResponse(ChallengeScheme.HTTP_BASIC,identity.getName(),identity.getPassword()));
      }
      if (cookie!=null) {
         request.getCookies().add(cookie);
      }
      return client.handle(request);
   }

   public void load(Document doc) {
      docBaseURI = doc.getBaseURI();
   }

   public void apply()
      throws Exception
   {
      apply(null);
   }
   public void apply(URI baseURI)
      throws Exception
   {
      root.apply(baseURI==null ? docBaseURI : baseURI);
   }
}
